#include "jni_utils.h"

namespace simple_renderer {

static JavaVM *g_jvm = nullptr;
static pthread_key_t g_jni_ptr;
static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
static jobject g_android_context = nullptr;

JavaVM *GetJVM() {
    return g_jvm;
}

JNIEnv* GetEnv() {
    void* env = nullptr;
    jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6);
    return reinterpret_cast<JNIEnv*>(env);
}

void InitAndroidContext(JNIEnv *env, jobject ctx){
    if(!g_android_context) {
        g_android_context = env->NewGlobalRef(ctx);
    }
}

jobject GetAndroidContext(){
    return g_android_context;
}

static void ThreadDestructor(void* prev_jni_ptr) {
    if (!GetEnv())
        return;

    jint status = g_jvm->DetachCurrentThread();
    if(status != JNI_OK){
        LOGE("[jni] Jvm DetachCurrentThread error status=%d", status);
    }
}

static void CreateJNIPtrKey() {
    int ret = pthread_key_create(&g_jni_ptr, &ThreadDestructor);
    if(ret != 0){
        LOGE("[jni] pthread_key_create error ret=%d", ret);
    }
}

/**
* called on onJniLoad
* @param jvm
*/
int InitRendererJvm(JavaVM* jvm){
    g_jvm = jvm;
    pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey);
    JNIEnv* jni = nullptr;
    if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK) {
        LOGE("[jni] GetEnv error");
        return -1;
    }

    return JNI_VERSION_1_6;
}

std::string GetThreadId() {
    char buf[21];
    snprintf(buf, sizeof(buf), "%ld", static_cast<long>(syscall(__NR_gettid)));
    return std::string(buf);
}

// Return the current thread's name.
std::string GetThreadName() {
    char name[17] = {0};
    if (prctl(PR_GET_NAME, name) != 0)
        return std::string("<noname>");
    return std::string(name);
}

JNIEnv* AttachCurrentThreadIfNeeded() {

    JNIEnv* jni = GetEnv();
    if (jni)
        return jni;
    if(!pthread_getspecific(g_jni_ptr)){
        LOGW("[jni] pthread_getspecific TLS has a JNIEnv* but not attached?");
    }

    std::string name(GetThreadName() + " - " + GetThreadId());
    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6;
    args.name = &name[0];
    args.group = nullptr;
    // Deal with difference in signatures between Oracle's jni.h and Android's.
#ifdef _JAVASOFT_JNI_H_  // Oracle's jni.h violates the JNI spec!
    void* env = nullptr;
#else
    JNIEnv* env = nullptr;
#endif
    int ret = g_jvm->AttachCurrentThread(&env, &args);
    if(ret != 0){
        LOGE("[jni] Jvm AttachCurrentThread error ret=%d", ret);
    }
    jni = reinterpret_cast<JNIEnv*>(env);
    ret = pthread_setspecific(g_jni_ptr, jni);
    if(ret != 0){
        LOGE("[jni] pthread_setspecific error ret=%d", ret);
    }
    return jni;
}


}